package ws

import (
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"go.uber.org/zap"
	"gogogo-server/engine/cororpc"
	"gogogo-server/engine/network/netproc"
	"sync"
)

type TypeMapConns map[*websocket.Conn]any
type TypeFnNewAgent func(conn netproc.IConn) cororpc.IGateAgent

// LGConn :LetGo Conn
type LGConn struct {
	conn      *websocket.Conn
	maxMsgLen uint32
	closeFlag bool
	writeChan chan []byte
	mu        sync.Mutex
}

// NewConn 创建一个新的连接,这个是在客户端连上来的协程里面调用的
func NewConn(conn *websocket.Conn, maxMsgLen uint32, sizeChan int) *LGConn {
	c := &LGConn{
		conn:      conn,
		maxMsgLen: maxMsgLen,
		closeFlag: false,
		writeChan: make(chan []byte, sizeChan),
	}
	go func() {
		for b := range c.writeChan {
			if b == nil {
				break
			}
			if err := c.conn.WriteMessage(websocket.BinaryMessage, b); err != nil {
				break
			}
		}
		conn.Close()
		c.mu.Lock()
		c.closeFlag = true
		c.mu.Unlock()
	}()
	return c
}

func (c *LGConn) ReadMsg() ([]byte, error) {
	_, b, e := c.conn.ReadMessage()
	return b, e
}

func (c *LGConn) doWrite(b []byte) {
	if len(c.writeChan) >= cap(c.writeChan) { // 写缓冲区满了
		zap.S().Warnf("%s 写缓冲区满了", c.RemoteAddr())
		c.doDestroy() // 直接销毁,这里可能是在业务协程里面的
		return
	}
	c.writeChan <- b
}

// WriteMsg 这个是会在逻辑的协程里面调用,需要加锁
func (c *LGConn) WriteMsg(data [][]byte) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.closeFlag { // 已经关闭
		return nil
	}
	var msgLen uint32 = 0
	for _, v := range data {
		msgLen += uint32(len(v))
	}
	if msgLen > c.maxMsgLen {
		return errors.New(fmt.Sprintf("%s 发送消息过长,data[0]=%s", c.RemoteAddr(), data[0]))
	} else if msgLen < 1 {
		return nil
	}

	if len(data) < 2 {
		c.doWrite(data[0])
		return nil
	}
	return nil
}

func (c *LGConn) LocalAddr() string {
	return c.conn.LocalAddr().String()
}
func (c *LGConn) RemoteAddr() string {
	return c.conn.RemoteAddr().String()
}

func (c *LGConn) Close() {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.closeFlag {
		return
	}
	c.doWrite(nil)
	c.closeFlag = true
}

func (c *LGConn) Destroy() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.doDestroy()
}

func (c *LGConn) doDestroy() {
	c.conn.Close()
	if !c.closeFlag {
		close(c.writeChan)
		c.closeFlag = true
	}
}
